package com.power.ding.service.impl;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.xxpt.gateway.shared.api.request.OapiGettokenJsonRequest;
import com.alibaba.xxpt.gateway.shared.api.request.OapiRpcOauth2DingtalkAppUserJsonRequest;
import com.alibaba.xxpt.gateway.shared.api.response.OapiGettokenJsonResponse;
import com.alibaba.xxpt.gateway.shared.api.response.OapiRpcOauth2DingtalkAppUserJsonResponse;
import com.alibaba.xxpt.gateway.shared.client.http.ExecutableClient;
import com.alibaba.xxpt.gateway.shared.client.http.IntelligentGetClient;
import com.alibaba.xxpt.gateway.shared.client.http.IntelligentPostClient;
import com.plf.common.cache.CacheManager;
import com.power.ding.dao.Di_todoDao;
import com.power.ding.exception.DingException;
import com.power.ding.model.Di_message;
import com.power.ding.model.Di_todo;
import com.power.ding.model.SystemParam;
import com.power.ding.model.enumeration.St_pStatus;
import com.power.ding.service.Di_messageService;
import com.power.ding.service.Di_todoService;
import com.power.ding.service.SystemService;
import com.power.sifa.approve.api.IAprv_process_materialsFeign;
import com.power.sifa.approve.model.enumeration.Tp_procmaterial;
import com.power.sifa.approve.model.enumeration.Tp_proctype;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpRequest;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class SystemServiceImpl implements SystemService {
    @Resource
    private CacheManager cacheManager;

    @Value("${ding.protocal:'http'}")
    private String protocal;

    @Value("${ding.domain:''}")
    private String domain;

    @Value("${ding.appkey:''}")
    private String appkey;

    @Value("${ding.appsecret:''}")
    private String appsecret;

    @Value("${ding.appcode:''}")
    private String appcode;

    @Value("${ding.appname:''}")
    private String appname;

    @Value("${ding.vkey:''}")
    private String vkey;

    @Value("${ding.apitime:''}")
    private String apitime;

    @Value("${ding.apihost:''}")
    private String apihost;

    private ExecutableClient executableClient;

    @Resource
    private Di_messageService di_messageService;

    @Resource
    private Di_todoService di_todoService;

    @Resource
    private Di_todoDao di_todoDao;

    @Resource
    private IAprv_process_materialsFeign iAprv_process_materialsFeign;

    /**
     * 钉钉内置http请求对象，保持单例
     *
     * @return
     */
    private ExecutableClient getExecutableClient() {
        if (executableClient == null) {
            executableClient = ExecutableClient.getInstance();
            executableClient.setDomainName(domain);
            executableClient.setProtocal(protocal);
            executableClient.setAccessKey(appkey);
            executableClient.setSecretKey(appsecret);
            executableClient.init();
        }
        return executableClient;
    }

    public String loadAccessToken() throws DingException {
        log.info("call loadAccessToken 获取access-token");
        String accesstoken = null;

        IntelligentGetClient intelligentGetClient = getExecutableClient().newIntelligentGetClient("/gettoken.json");
        OapiGettokenJsonRequest oapiGettokenJsonRequest = new OapiGettokenJsonRequest();
        oapiGettokenJsonRequest.setAppkey(appkey);
        oapiGettokenJsonRequest.setAppsecret(appsecret);
        OapiGettokenJsonResponse apiResult = intelligentGetClient.get(oapiGettokenJsonRequest);

        if (apiResult.getSuccess()) {
            String data = apiResult.getContent().getData();
            log.info("data: {}", data);
            JSONObject datajson = JSON.parseObject(data);

            accesstoken = datajson.getString("accessToken");
            Integer expiresIn = datajson.getInteger("expiresIn");
            if (StringUtils.hasLength(accesstoken)) {
                expiresIn -= 60; // 少算60秒
                cacheManager.put_temp(SystemParam.CACHE_ACCESS_TOKEN, accesstoken, expiresIn);
            }
        } else {
            throw new DingException(apiResult.getMessage());
        }
        log.info("accesstoken: {}", accesstoken);
        return accesstoken;
    }

    @Override
    public String getAccessToken() throws DingException {
        String access_token = (String) cacheManager.get(SystemParam.CACHE_ACCESS_TOKEN);
        if (access_token == null) {
            access_token = loadAccessToken();
        }
        return access_token;
    }

    @Override
    public JSONObject getAppUser(String auth_code) throws DingException {
        log.info("call getAppUser 获取用户信息 auth_code: {}", auth_code);
        JSONObject user = null;
        String access_token = getAccessToken();
        Assert.notNull(access_token, "access_token不得为空");

        IntelligentPostClient intelligentPostClient = getExecutableClient().newIntelligentPostClient("/rpc/oauth2/dingtalk_app_user.json");
        OapiRpcOauth2DingtalkAppUserJsonRequest oapiRpcOauth2DingtalkAppUserJsonRequest = new OapiRpcOauth2DingtalkAppUserJsonRequest();
        oapiRpcOauth2DingtalkAppUserJsonRequest.setAccess_token(access_token);
        oapiRpcOauth2DingtalkAppUserJsonRequest.setAuth_code(auth_code);
        OapiRpcOauth2DingtalkAppUserJsonResponse apiResult = intelligentPostClient.post(oapiRpcOauth2DingtalkAppUserJsonRequest);
        if (apiResult.getSuccess()) {
            user = JSON.parseObject(apiResult.getContent());
        } else {
            throw new DingException(apiResult.getMessage());
        }
        return user;
    }

    @Override
    public Long getTime() throws DingException {
        log.info("call getTime 获取服务器时间");
        Long tm = null;
        String response = HttpRequest.get(apitime).execute().body();
        JSONObject apiResult = JSON.parseObject(response);
        if (apiResult.getString("type").equals("success")) {
            String data = apiResult.getString("data");
            tm = Long.parseLong(data);
            log.info("服务器时间 {} {}", tm, DateUtil.formatDateTime(new Date(tm)));
        } else {
            throw new DingException(apiResult.getString("message"));
        }
        return tm;
    }

    /**
     * 返回固定参数
     *
     * @return
     * @throws DingException
     */
    private Map<String, String> getRegularParam() throws DingException {
        Map<String, String> param = new HashMap<String, String>();
//		Long timestamp=System.currentTimeMillis();
        Long timestamp = this.getTime();
        String token = UUID.randomUUID().toString(true);
        String vcode = DigestUtil.md5Hex(timestamp + appcode + token + vkey);

        param.put("timestamp", timestamp + "");
        param.put("token", token);
        param.put("app", appcode);
        param.put("vcode", vcode);
        return param;
    }

    private static final String URL_MESSAGE_SEND = "%s/messageService/serviceapi/message/add?timestamp=%s&token=%s&app=%s&vcode=%s";

    @Override
    public String sendMessage(String id) throws DingException {
        log.info("call sendMessage 发送消息");
        Di_message msg = di_messageService.getById(id);
        if (msg == null) {
            throw new DingException("消息记录不存在 id=" + id);
        }

        JSONObject msgjson = (JSONObject) JSON.toJSON(msg);

        msgjson.put("appCode", appcode);
        msgjson.put("appName", appname);
        msgjson.put("vendorId", id);
        msgjson.remove("id");
        msgjson.remove("sendTime");

        String remoteid = null;
        Map<String, String> param = getRegularParam();
        String url = String.format(URL_MESSAGE_SEND, apihost,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"));
        String msgbody = JSON.toJSONString(msgjson);

        log.info("sendMessage");
        log.info("url: {}", url);
        log.info("msgbody: {}", msgbody);

        String response = HttpRequest.post(url)
                .body(msgbody)
                .execute().body();
        JSONObject apiResult = JSON.parseObject(response);
        log.info("apiResult: {}", apiResult);

        if (apiResult.getString("type").equals("success")) {
            remoteid = apiResult.getString("metadata");
            log.info("统一消息平台中心的id {} ", remoteid);
            msg.setRemoteid(remoteid);
            msg.setSendTime(Calendar.getInstance().getTime());
            msg.setSendError(null);
            di_messageService.saveOrUpdate(msg);
        } else {
            String error = apiResult.getString("message");
            msg.setSendTime(null);
            msg.setRemoteid(null);
            msg.setSendError(error);
            di_messageService.saveOrUpdate(msg);
            throw new DingException();
        }
        return remoteid;
    }

    private static final String URL_MESSAGE_GET = "%s/messageService/serviceapi/message/get/%s?timestamp=%s&token=%s&app=%s&vcode=%s";

    @Override
    public JSONObject getMessage(String id) throws DingException {
        log.info("call getMessage 获取消息");
        JSONObject re = null;
        Map<String, String> param = getRegularParam();
        String url = String.format(URL_MESSAGE_GET, apihost, id,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"));

        String response = HttpRequest.get(url)
                .execute().body();
        re = JSON.parseObject(response);
        return re;
    }

    private static final String URL_TODO_SEND = "%s/todoService/serviceapi/todo/add?timestamp=%s&token=%s&app=%s&vcode=%s";

    @Override
    public void sendTodo(String id) throws DingException {
        log.info("call sendTodo 发送待办");
        Di_todo todo = di_todoService.getById(id);
        if (todo == null) {
            throw new DingException("待办记录不存在 id=" + id);
        }

        JSONObject todojson = (JSONObject) JSON.toJSON(todo);

        todojson.put("appCode", appcode);
        todojson.put("appName", appname);
        todojson.put("vendorId", id);
        todojson.put("receiveTime", DateUtil.now());
        todojson.put("mobileUrl", todojson.get("url"));
        todojson.remove("id");
        todojson.remove("sendTime");

//		String remoteid=null;
        Map<String, String> param = getRegularParam();
        String url = String.format(URL_TODO_SEND, apihost,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"));
        String msgbody = JSON.toJSONString(todojson);

        log.info("sendTodo");
        log.info("url: {}", url);
        log.info("msgbody: {}", msgbody);

        String response = HttpRequest.post(url)
                .body(msgbody)
                .execute().body();
        JSONObject apiResult = JSON.parseObject(response);
        log.info("apiResult: {}", apiResult);

        if (apiResult.getString("type").equals("success")) {
            todo.setSendTime(Calendar.getInstance().getTime());
            todo.setSendError(null);
            di_todoService.saveOrUpdate(todo);
        } else {
            String error = apiResult.getString("message");
            JSONObject data = apiResult.getJSONObject("data");
            if (data != null && data.getString("errorMessage") != null) {
                error = data.getString("errorMessage");
            }

            todo.setSendTime(null);
            todo.setSendError(error);
            di_todoService.saveOrUpdate(todo);
            throw new DingException();
        }
    }

    private SimpleDateFormat dtsFmt = new SimpleDateFormat("yyyyMMddHHmmssSSS");
    private static final String URL_TODO_FINISH = "%s/todoService/serviceapi/todo/done/%s/%s?timestamp=%s&token=%s&app=%s&vcode=%s";

    @Override
    public JSONObject finishTodo(String id) throws DingException {
        log.info("call finishTodo 完成待办");

        Calendar now=Calendar.getInstance();
        String donetime=dtsFmt.format(now.getTime());

        Map<String, String> param=getRegularParam();
        String url=String.format(URL_TODO_FINISH, apihost, id, donetime,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"));

        log.info("finishTodo");
        log.info("url: {}", url);
        String response=HttpRequest.get(url)
                .execute().body();
        JSONObject apiResult=JSON.parseObject(response);
        log.info("apiResult: {}", apiResult);
        return apiResult;
    }

    private static final String URL_OPEN_AUTH = "%s/platform/ddopen/authorize?timestamp=%s&token=%s&app=%s&vcode=%s&uid=%s&response_type=code&redirect_uri=%s";

    @Override
    public String getOpenAuthorizeUrl(String employeeCode, String redirect) throws DingException {
        log.info("call getOpenAuthorizeUrl 构造授权URL");
        Map<String, String> param = getRegularParam();
        String url = String.format(URL_OPEN_AUTH, apihost,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"), employeeCode, redirect);
        log.info("AuthorizeUrl: {}", url);
        return url;
    }

    private static final String URL_OPEN_TOKEN = "%s/websso/ddopen/token?timestamp=%s&token=%s&app=%s&vcode=%s&grant_type=authorization_code&code=%s";

    /**
     * 通过授权码，获取token
     *
     * @param code
     * @return
     * @throws DingException
     */
    private JSONObject getOpenToken(String code) throws DingException {
        log.info("call getOpenToken 获取授权token");
        Map<String, String> param = getRegularParam();
        String url = String.format(URL_OPEN_TOKEN, apihost,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"), code);
        String response = HttpRequest.get(url)
                .execute().body();
        JSONObject tokenResult = JSON.parseObject(response);
        log.info("tokenResult: {}", tokenResult);
        return tokenResult;
    }

    private static final String URL_OPEN_USER = "%s/websso/ddopen/userinfo?timestamp=%s&token=%s&app=%s&vcode=%s&uid=%s&access_token=%s";

    @Override
    public JSONObject getOpenUser(String code) throws DingException {
        log.info("call getOpenUser 获取授权用户信息");
        JSONObject user = null;
        JSONObject token = getOpenToken(code);
        String access_token = token.getString("access_token");
        String uid = token.getString("uid");

        if (access_token != null && uid != null) {
            Map<String, String> param = getRegularParam();
            String url = String.format(URL_OPEN_USER, apihost,
                    param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"), uid, access_token);
            String response = HttpRequest.get(url)
                    .execute().body();
            JSONObject apiResult = JSON.parseObject(response);
            log.info("apiResult: {}", apiResult);

            if (apiResult.getString("type").equals("success")) {
                user = apiResult.getJSONObject("data");
            } else {
                String error = apiResult.getString("message");
                throw new DingException(error);
            }
        } else {
            throw new DingException("获取open token失败");
        }
        return user;
    }

    @Override
    public Map<String, Object> getTodo(String id) throws DingException {
        return di_todoDao.getTodo(id);
    }

    private static final String URL_TODO_DEL = "%s/todoService/serviceapi/todo/delete/%s?timestamp=%s&token=%s&app=%s&vcode=%s";

	@Override
	public JSONObject delTodo(String id) throws DingException {
		log.info("call delTodo 删除待办");

        Di_todo todo = di_todoService.getById(id);
        if (todo == null) {
            throw new DingException("待办记录不存在 id=" + id);
        }

        Map<String, String> param = getRegularParam();
        String url = String.format(URL_TODO_DEL, apihost, id,
                param.get("timestamp"), param.get("token"), param.get("app"), param.get("vcode"));
        log.info("delTodo");
        log.info("url: {}", url);
        String response = HttpRequest.delete(url)
                .execute().body();
        JSONObject apiResult = JSON.parseObject(response);
        log.info("apiResult: {}", apiResult);

        if (apiResult.getString("type").equals("success")) {
            todo.setPStatus(St_pStatus.DELETED);
            todo.setPTime(Calendar.getInstance().getTime());
            di_todoService.saveOrUpdate(todo);
        }
        return apiResult;
	}

}
